//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 1995-1999  Microsoft Corporation

Module Name:

voice.cpp

Abstract:


Notes:


--*/
/*
------------------------------------------------------------------------------
    Revision History
    Date                Author            Activity ID            Activity Headline
    2006-03-16          Wangxiaogang      CEDB00010042           W3K RIL Porting
    2006-03-23          Wangxiaogang      CEDB00010827           Adjust volume in w3k                
    2007-01-08          qinxuetao         CEDB00040729           Porting ril driver for CC/SMS  
    2007-04-16          Gaorenhui         WM600002430            Resolved CC Audio Gain issue on WAVECOM.
    2007-04-28          qinxuetao         WM600003778            Modify func ConvertAudioRxGain to extern visibility
    2007-05-10          qinxuetao         WM600003334            If muting has been set, Dragonfly needs setting again after any call is retrieved or connected.
    2007-06-28          Qinxuetao         WM600006133            Modify function ConvertAudioRxGain
------------------------------------------------------------------------------
*/

#include "precomp.h"

#if defined(WAVECOM_DRIVER) || defined(OEM1_DRIVER) || defined(OEM2_DRIVER) || defined(EMP_DRIVER)
#include <gsmradio.h>
#else
#define IOCTL_SPKR_ENABLE   0
#define IOCTL_SPKR_DISABLE  0
#endif


#if defined(OEM1_DRIVER) || defined(OEM2_DRIVER) || defined(EMP_DRIVER) || defined(WAVECOM_DRIVER)
// This driver doesn't have a way to query current
// uplink and downlink volume, so we have to cache them.
DWORD   g_dwCacheUplinkVolume;
DWORD   g_dwCacheDownlinkVolume;
#endif

#if defined (RIL_CACHE_AUDIO_MUTING) || defined (WAVECOM_DRIVER)
// Cache the mute setting to avoid querying the radio
BOOL   g_fAudioMutingOn;
#endif

//
//  Mapped Audio Gain Value between Mobile and Modem.
//
DWORD g_dwRxGainMap[]=
{ 
  0x0,          /*+VGR: 0*/
  0x0,          /*+VGR: 1*/   
  0x33333333,   /*+VGR: 2*/
  0x33333333,   /*+VGR: 3*/
  0x66666666,   /*+VGR: 4*/
  0x66666666,   /*+VGR: 5*/
  0x99999999,   /*+VGR: 6*/
  0x99999999,   /*+VGR: 7*/
  0xCCCCCCCC,   /*+VGR: 8*/
  0xCCCCCCCC,   /*+VGR: 9*/
  0xFFFFFFFF    /*+VGR: 10*/
};
#define RxGainMapSize  (sizeof(g_dwRxGainMap)/sizeof(*g_dwRxGainMap))
DWORD ConvertAudioRxGain(DWORD dwInValue, BOOL fMobileToModem)
{
    DWORD dwGain = dwInValue;
#if defined(WAVECOM_DRIVER)
    if (fMobileToModem)
    {
        // WaveCOM's range is: 1 ~ 10
        for(dwGain=1; dwGain<RxGainMapSize; dwGain++)
        {
            if(dwInValue <= g_dwRxGainMap[dwGain]) break; 
        }
        g_dwCacheDownlinkVolume = g_dwRxGainMap[dwGain];
    }
    else
    {
        dwGain = g_dwRxGainMap[ (dwInValue<RxGainMapSize)? dwInValue: (RxGainMapSize-1) ];
        g_dwCacheDownlinkVolume = dwGain;
    }
#endif
    return dwGain;
}

//
//
//
static HRESULT ParseGetAudioGain(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetAudioGain);
    UINT nValue;
    RILGAININFO* prgi = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prgi = (RILGAININFO*)AllocBlob(sizeof(RILGAININFO));
    if (!prgi) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prgi, 0x00, sizeof(RILGAININFO));
    prgi->cbSize = sizeof(RILGAININFO);

#if defined(OEM1_DRIVER)
    nValue = g_dwCacheDownlinkVolume;
#elif defined(OEM2_DRIVER)
    nValue = (g_dwCacheDownlinkVolume * 255) / 100;
#elif defined(EMP_DRIVER)
    nValue = (g_dwCacheDownlinkVolume * 255) / 8;
#else
    // Parse "<prefix>+VGR: <gain><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                 ||
        !MatchStringBeginning(szRsp, "+VGR: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)        ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
#endif
#if defined(WAVECOM_DRIVER)
    nValue = (UINT)ConvertAudioRxGain(nValue, FALSE);  // Added by Gaorenhui@2007-04-13
    prgi->dwRxGain = nValue; 
#else
    prgi->dwRxGain = (nValue << 24) | (nValue << 16) | (nValue << 8) | (nValue);
#endif
    prgi->dwParams |= RIL_PARAM_GI_RXGAIN;

#if defined(OEM1_DRIVER) || defined(OEM2_DRIVER) || defined(EMP_DRIVER)
    nValue = g_dwCacheUplinkVolume; // For OEM1 and OEM2 and EMP, only cache TxGain value in gsm driver, no AT command is sent out.
#else
    // Parse "<prefix>+VGT: <gain><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                 ||
        !MatchStringBeginning(szRsp, "+VGT: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)        ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
#endif

#ifndef OEM2_DRIVER // OEM2 does not offer a transmit gain AT command by default
    prgi->dwTxGain = (nValue << 24) | (nValue << 16) | (nValue << 8) | (nValue);
    prgi->dwParams |= RIL_PARAM_GI_TXGAIN;
#endif

    pBlob = (void*)prgi;
    cbBlob = sizeof(RILGAININFO);

Error:
    if (FAILED(hr)) {
        FreeBlob(prgi);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetAudioGain(DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetAudioGain);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

#if defined(OEM1_DRIVER) || defined(OEM2_DRIVER) || defined(EMP_DRIVER) ||defined(WAVECOM_DRIVER)
    if (!QueueCmd(pHandle, "AT\r", CMDOPT_NONE, APIID_GETAUDIOGAIN, ParseGetAudioGain, NULL, hr)) {
#else
    if (!QueueCmd(pHandle, "AT+VGR?;+VGT?\r", CMDOPT_NONE, APIID_GETAUDIOGAIN, ParseGetAudioGain, NULL, hr)) {
#endif
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

//
//
//
HRESULT RILDrv_SetAudioGain(DWORD dwParam, const RILGAININFO* lpGainInfo)
{
    FUNCTION_TRACE(RILDrv_SetAudioGain);
    char szCmd[MAX_PATH];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !lpGainInfo) {
        hr = E_FAIL;
        goto Error;
    }

    if (!(lpGainInfo->dwParams & RIL_PARAM_GI_RXGAIN) && !(lpGainInfo->dwParams & RIL_PARAM_GI_TXGAIN)) {
        hr = E_INVALIDARG;
        goto Error;
    }

    (void)strncpyz(szWalk, "AT", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    if (lpGainInfo->dwParams & RIL_PARAM_GI_RXGAIN) {
#if defined(WAVECOM_DRIVER)
        // Wavecom module specifies gain opposite GSM 07.07, C.2.4
        DWORD dwGain;
        dwGain = ConvertAudioRxGain(lpGainInfo->dwRxGain, TRUE);
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "+VGR=%u", dwGain);
#else // Standard GSM case, used for OEM1 and OEM2 and EMP
        g_dwCacheDownlinkVolume = (lpGainInfo->dwRxGain >> 24);
#ifdef OEM2_DRIVER
        // downlink volume on OEM2 is in the range 0-100, not 0-255, so nomalize here.
        g_dwCacheDownlinkVolume = (g_dwCacheDownlinkVolume*100)/255;
#elif defined(EMP_DRIVER)
        // downlink volume on EMP is in the range 0-8, not 0-255, so nomalize here.
        g_dwCacheDownlinkVolume = (g_dwCacheDownlinkVolume*8)/255;
#endif // OEM2_DRIVER
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "+CLVL=%u", g_dwCacheDownlinkVolume);
#endif // Standard GSM case
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);

#if !defined(OEM2_DRIVER) && !defined(OEM1_DRIVER) && !defined(EMP_DRIVER)
        if (lpGainInfo->dwParams & RIL_PARAM_GI_TXGAIN) {
            (void)strncpyz(szWalk, ";", MAX_PATH - (szWalk - szCmd));
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            DEBUGCHK(NULL != szWalk);
        }
#endif
    }

#if !defined(OEM2_DRIVER) && !defined(OEM1_DRIVER) && !defined(EMP_DRIVER)&&!defined(WAVECOM_DRIVER) // OEM1 and OEM2 and EMP do not support an AT command to modify Tx gain
    if (lpGainInfo->dwParams & RIL_PARAM_GI_TXGAIN) {
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "+VGT=%u", (lpGainInfo->dwTxGain >> 24));

        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }
#else // OEM1 and OEM2 and EMP
    g_dwCacheUplinkVolume = lpGainInfo->dwTxGain;   // For OEM1 and OEM2 and EMP, only cache TxGain value in gsm driver, no AT command is sent out.
#endif
    
    (void)strncpyz(szWalk, "\r", MAX_PATH - (szWalk - szCmd));  // NO_TYPO: 30

    if (!QueueCmd(pHandle, szCmd, CMDOPT_NONE, APIID_SETAUDIOGAIN, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SetAudioDevices(DWORD dwParam, const RILAUDIODEVICEINFO* lpAudioDeviceInfo)
{
    FUNCTION_TRACE(RILDrv_SetAudioDevices);
#if defined(WAVECOM_DRIVER) || defined(OEM1_DRIVER)
    // HW-SPECIFIC: This uses WaveCom-specific IOCTLs

    DWORD dwDummy;
    CComHandle* pComDevice;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !lpAudioDeviceInfo) {
        hr = E_FAIL;
        goto Error;
    }

    if (!(lpAudioDeviceInfo->dwParams & RIL_PARAM_ADI_TXDEVICE) &&
        !(lpAudioDeviceInfo->dwParams & RIL_PARAM_ADI_RXDEVICE)) {
        hr = E_INVALIDARG;
        goto Error;
    }

    if (lpAudioDeviceInfo->dwParams & RIL_PARAM_ADI_TXDEVICE) {
        DEBUGCHK(NULL != pHandle->GetDevice());
        DEBUGCHK(NULL != pHandle->GetDevice()->GetComDevice());
        pComDevice = pHandle->GetDevice()->GetComDevice();

        if (RIL_AUDIO_NONE == lpAudioDeviceInfo->dwTxDevice) {
            (void)pComDevice->IOControl(IOCTL_SPKR_DISABLE, NULL, 0, NULL, 0, dwDummy);
        } else {
            (void)pComDevice->IOControl(IOCTL_SPKR_ENABLE, NULL, 0, NULL, 0, dwDummy);
        }
    }

    // Send a no-op AT command to force an OK response
    if (!QueueCmd(pHandle, NULL, CMDOPT_NOOP, APIID_SETAUDIODEVICES, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }
Error:

#else  // defined(WAVECOM_DRIVER) || defined(OEM1_DRIVER) 

    HRESULT hr = E_NOTIMPL;
#endif // defined(WAVECOM_DRIVER) || defined(OEM1_DRIVER)

    return hr;
}


//
//
//
static HRESULT ParseGetAudioMuting(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetAudioMuting);
    BOOL* pfEnabled = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pfEnabled = (BOOL*)AllocBlob(sizeof(BOOL));
    if (!pfEnabled) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pfEnabled, 0x00, sizeof(BOOL));


#ifdef RIL_CACHE_AUDIO_MUTING

    *pfEnabled = g_fAudioMutingOn;

#else // RIL_CACHE_AUDIO_MUTING

    UINT nValue;

    // Parse "<prefix>+CMUT: <enabled><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                           ||
        !MatchStringBeginning(szRsp, "+CMUT: ", szRsp)          ||
        !ParseUIntAndVerifyAbove(szRsp, TRUE, 2, nValue, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    *pfEnabled = (1 == nValue);

#endif // RIL_CACHE_AUDIO_MUTING

#ifdef WAVECOM_DRIVER
    g_fAudioMutingOn = *pfEnabled;
#endif

    pBlob = (void*)pfEnabled;
    cbBlob = sizeof(BOOL);

Error:
    if (FAILED(hr)) {
        FreeBlob(pfEnabled);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetAudioMuting(DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetAudioMuting);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

#ifdef RIL_CACHE_AUDIO_MUTING

    if (!QueueCmd(pHandle,  NULL, CMDOPT_NOOP, APIID_GETAUDIOMUTING, ParseGetAudioMuting, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }   

#else   // RIL_CACHE_AUDIO_MUTING

#ifdef WAVECOM_DRIVER
    //Clear g_fAudioMutingOn before query
    g_fAudioMutingOn = FALSE;
#endif
#ifdef RIL_RADIO_RESILIENCE
    if (ShouldSpoofCommand())
    {
        // If the radio was reset recently, just return unmuted.  Otherwise this may get
        // queued up after all the init commands, and take a long time to execute
        // This is bad, since we want to handle the RIL_NOTIFY_DISCONNECT 
        // that is sent up during reset so the UI can be updated quickly        
        BOOL* pfEnabled = (BOOL*)AllocBlob(sizeof(BOOL));
        if (!pfEnabled) {
            hr = E_OUTOFMEMORY;
            goto Error;
        }
        hr = pHandle->GetDevice()->GetNextCmdID();
        *pfEnabled = FALSE;
        pHandle->Notify(RIL_RESULT_OK, hr, pfEnabled, sizeof(BOOL));
    }
    else
#endif // RIL_RADIO_RESILIENCE
    {
        if (!QueueCmd(pHandle, "AT+CMUT?\r", CMDOPT_NONE, APIID_GETAUDIOMUTING, ParseGetAudioMuting, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }

#endif  // RIL_CACHE_AUDIO_MUTING

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SetAudioMuting(DWORD dwParam, BOOL fEnable)
{
    FUNCTION_TRACE(RILDrv_SetAudioMuting);
    char szCmd[MAX_PATH];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    (void)_snprintfz(szCmd, MAX_PATH, "AT+CMUT=%u\r", (fEnable ? 1 : 0));
    if (!QueueCmd(pHandle, szCmd, CMDOPT_NONE, APIID_SETAUDIOMUTING, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }
#if defined (RIL_CACHE_AUDIO_MUTING)   
    else {
        g_fAudioMutingOn = fEnable;
    }
#endif  // RIL_CACHE_AUDIO_MUTING

Error:
    return hr;
}
